﻿using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shell;
using Red.Controls.Shell;
using Red.Launcher.Menus.Packages;
using Red.Launcher.Utilities;
using Database = Red.Launcher.ViewModels.Workspaces.Database;
using Dialog = Red.Launcher.Utilities.Dialog;
using ViewModel = Red.Launcher.ViewModels.Packages;

namespace Red.Launcher
{
    public partial class PackagesView : UserControl, INotifyPropertyChanged
    {
        #region Private Data

        private Database.RatingConfigurationGroup _ratingConfigurations = null;
        private Dialog.Manager _dialogs = new Dialog.Manager();

        #endregion Private Data

        #region Public Properties

        public AvailablePackagesList ToolkitPackagesControl => _toolkitPackages;
        public AvailablePackagesList EnginePackagesControl => _enginePackages;
        public AvailablePackagesList EditorPackagesControl => _wxEditorPackages;

        public ViewModel.Manager ActiveToolkitOrEditorManager => ( _toolkitPackages.ActivePackage != null ) ? _toolkitPackages.Manager : _wxEditorPackages.Manager;

        public Database.User User
        {
            get { return ToolkitPackagesControl.Manager.User; }
            set
            {
                ToolkitPackagesControl.User = value;
                EnginePackagesControl.User = value;
                EditorPackagesControl.User = value;

                _dialogs.User = value;
                AppendToolkitAdminContextMenu();
                OnPropertyChanged();
            }
        }

        public string ActivePackageName => $"{ActiveToolkitOrEditorManager.ActivePackageName}\n{_enginePackages.Manager.ActivePackageName}";

        public bool IsAnyPackageInstalling => ToolkitPackagesControl.Manager.IsInstallationInProgress || EnginePackagesControl.Manager.IsInstallationInProgress || EditorPackagesControl.Manager.IsInstallationInProgress;

        public TaskbarItemProgressState IsAnyPackageInstallingTaskbarState => IsAnyPackageInstalling ? TaskbarItemProgressState.Normal : TaskbarItemProgressState.None;

        public double LowestInstallProgress => CalcLowestInstallProgress();

        public Diagnostics.Process.Manager PackageProcessManager = new Diagnostics.Process.Manager();

        #endregion Public Properties

        #region C-Tor

        partial void InitializeAnalytics();

        public PackagesView()
        {
            InitializeComponent();

            Loaded += PackagesView_Loaded;

            Settings.StaticPropertyChanged += Settings_StaticPropertyChanged;

            _toolkitPackages.Manager.PropertyChanged += Manager_PropertyChanged;
            _enginePackages.Manager.PropertyChanged += Manager_PropertyChanged;
            _wxEditorPackages.Manager.PropertyChanged += Manager_PropertyChanged;

            _toolkitPackages.PackagesRefreshed += PackagesRefreshed;

            _adminVoteRatingsConfiguration.Click += _adminVoteRatingsConfiguration_Click;

            InitializeAnalytics();
        }

        #endregion C-Tor

        #region Public Methods

        public async Task PerformFirstTimeFetch()
        {
            await _toolkitPackages.Manager.PerformFirstTimeFetch();
            await _enginePackages.Manager.PerformFirstTimeFetch();
            await _wxEditorPackages.Manager.PerformFirstTimeFetch();

            if ( _ratingConfigurations == null )
            {
                await RefreshRatingsConfigurations();
            }
        }

        public double CalcLowestInstallProgress()
        {
            double lowestProgress = 1.0;

            if ( _toolkitPackages.Manager.IsInstallationInProgress && lowestProgress > _toolkitPackages.Manager.LowestInstallingPackageProgress )
            {
                lowestProgress = _toolkitPackages.Manager.LowestInstallingPackageProgress;
            }

            if ( _enginePackages.Manager.IsInstallationInProgress && lowestProgress > _enginePackages.Manager.LowestInstallingPackageProgress )
            {
                lowestProgress = _enginePackages.Manager.LowestInstallingPackageProgress;
            }

            if ( _wxEditorPackages.Manager.IsInstallationInProgress && lowestProgress > _wxEditorPackages.Manager.LowestInstallingPackageProgress )
            {
                lowestProgress = _wxEditorPackages.Manager.LowestInstallingPackageProgress;
            }

            return lowestProgress;
        }

        #endregion Public Methods

        #region RedMainWindow implementation

        public void WriteToSettings( UserSettings settings )
        {
            settings.ActiveToolkitBuildId = _toolkitPackages.Manager.ActivePackageBuildId;
            settings.ActiveEngineBuildId = _enginePackages.Manager.ActivePackageBuildId;
            settings.ActiveEditorBuildId = _wxEditorPackages.Manager.ActivePackageBuildId;
        }

        public void LoadFromSettings( UserSettings settings )
        {
            string path = System.IO.Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Data", settings.ProcessMonitorXml );
            PackageProcessManager.Load( path );
            Diagnostics.Process.Monitor uninstallMonitor = PackageProcessManager[ "uninstall_collection" ];

            _toolkitPackages.Manager.AppConfig = settings.ToolkitPackageConfig;
            _toolkitPackages.Manager.ActivePackageBuildId = settings.ActiveToolkitBuildId;
            _toolkitPackages.Manager.InstallationDirectory = settings.ToolkitPackagesInstallationDirectory;
            _toolkitPackages.UninstallMonitor = uninstallMonitor;

            _enginePackages.Manager.AppConfig = settings.EnginePackageConfig;
            _enginePackages.Manager.ActivePackageBuildId = settings.ActiveEngineBuildId;
            _enginePackages.Manager.InstallationDirectory = settings.EnginePackagesInstallationDirectory;
            _enginePackages.UninstallMonitor = uninstallMonitor;

            _wxEditorPackages.Manager.AppConfig = settings.EditorPackageConfig;
            _wxEditorPackages.Manager.ActivePackageBuildId = settings.ActiveEditorBuildId;
            _wxEditorPackages.Manager.InstallationDirectory = settings.EditorPackagesInstallationDirectory;
            _wxEditorPackages.UninstallMonitor = uninstallMonitor;
        }

        #endregion

        #region Private Methods

        private async Task RefreshRatingsConfigurations()
        {
            _ratingConfigurations = new Database.RatingConfigurationGroup();
            await _ratingConfigurations.Fetch( RatingsConfigurations_FetchComplete );
        }

        private void AppendToolkitAdminContextMenu()
        {
            ContextMenu menu = _toolkitPackages.DataGrid.TryFindResource( "DataRowContextMenu" ) as ContextMenu;

            if ( menu != null )
            {
                if ( _toolkitPackages.Manager.User?.IsAdmin == true )
                {
                    MenuItem admin = null;

                    //Remove the existing one, if it's already there
                    foreach ( MenuItem item in menu.Items )
                    {
                        if ( item.Name == "Admin" )
                        {
                            admin = item;
                            break;
                        }
                    }

                    double iconSize =(double)FindResource( "DefaultContextMenuIconSize" );

                    MenuItem launcherMinVersion = new MenuItem()
                    {
                        Header = "Mark as minimum required version for Red Launcher",
                        Icon = new Red.Controls.RedIcon() { Canvas = (Canvas)FindResource( "appbar_tag" ), Width = iconSize, Height = iconSize }
                    };

                    launcherMinVersion.Click += LauncherMinVersion_Click;

                    admin.Items.Add( launcherMinVersion );
                }
            }
        }

        private static void SetRatingsConverter( AvailablePackagesList packagesListView, Database.RatingConfigurationGroup ratingConfiguration )
        {
            ViewModel.StabilityConverter converter = packagesListView.TryFindResource( "StabilityConverter" ) as ViewModel.StabilityConverter;

            if ( converter != null )
            {
                converter.Evaluator = new ViewModel.StabilityEvaluator();

                converter.Evaluator.OnAnalysisComplete += () =>
                {
                    packagesListView.InvalidateRatings();
                };

                converter.Evaluator.Configuration = ratingConfiguration;
            }
        }

        #endregion Private Methods

        #region Event Handlers
        
        private async void PackagesRefreshed()
        {
            await RefreshRatingsConfigurations();
        }

        private void Settings_StaticPropertyChanged( object sender, PropertyChangedEventArgs e )
        {
            if ( e.PropertyName == "ToolkitPackagesInstallationDirectory" )
            {
                _toolkitPackages.Manager.InstallationDirectory = Settings.ToolkitPackagesInstallationDirectory.ResolvedPath;
            }
            else if ( e.PropertyName == "EnginePackagesInstallationDirectory" )
            {
                _enginePackages.Manager.InstallationDirectory = Settings.EnginePackagesInstallationDirectory.ResolvedPath;
            }
        }

        private void Manager_PropertyChanged( object sender, PropertyChangedEventArgs e )
        {
            ViewModel.Manager packageManager = (ViewModel.Manager)sender;

            if ( e.PropertyName == "PackageTitle" || e.PropertyName == "ActivePackage" )
            {
                OnPropertyChanged( "ActivePackageName" );

                // The following check ensure that's only one editor or one toolkit package can be active at a time
                if ( e.PropertyName == "ActivePackage" )
                {
                    ViewModel.Manager manager = (ViewModel.Manager)sender;

                    if ( manager != _enginePackages.Manager )
                    {
                        ViewModel.Manager altManager = ( manager == _wxEditorPackages.Manager ) ? _toolkitPackages.Manager : _wxEditorPackages.Manager;

                        if ( manager.ActivePackage != null && altManager.ActivePackage != null )
                        {
                            altManager.ActivePackage.IsActive = false;
                        }
                    }
                }
            }
            else if( e.PropertyName == "LowestInstallingPackageProgress" || e.PropertyName == "IsInstallationInProgress" )
            {
                OnPropertyChanged( "IsAnyPackageInstalling" );
                OnPropertyChanged( "LowestInstallProgress" );
                OnPropertyChanged( "IsAnyPackageInstallingTaskbarState" );
            }
        }

        private void RatingsConfigurations_FetchComplete()
        {
            SetRatingsConverter( _toolkitPackages, _ratingConfigurations );
            SetRatingsConverter( _enginePackages, _ratingConfigurations );
            SetRatingsConverter( _wxEditorPackages, _ratingConfigurations );
        }

        private void PackagesView_Loaded( object sender, RoutedEventArgs e )
        {
            Window parentWindow = Window.GetWindow( this );
            parentWindow.Closing += parentWindow_Closing;
            _dialogs.Owner = parentWindow;
        }

        private void parentWindow_Closing( object sender, CancelEventArgs e )
        {
            _dialogs.CloseAll();
        }

        private void _adminVoteRatingsConfiguration_Click( object sender, RoutedEventArgs e )
        {
            _dialogs.Show<VoteRatingsConfigurationDialog>( VoteRatingsConfigurationDialogClosed, true );
        }

        private async void VoteRatingsConfigurationDialogClosed( Dialog.IEvents dialog, Dialog.CloseReason reason )
        {
            if( reason == Dialog.CloseReason.Ok )
            {
                await RefreshRatingsConfigurations();
            }
        }

        private async void LauncherMinVersion_Click( object sender, RoutedEventArgs e )
        {
            ViewModel.Package pkg = (ViewModel.Package)_toolkitPackages.DataGrid.SelectedItem;

            string message = $"Are you sure you want to set Launcher with build id {pkg.BuildId} as the minimum required version? This will force all users using an older version to update";
            string title = @"Set minimum required version";

            bool setVersionConfirmed = RedMessageBox.Show( this, RedDialogType.YesNo, message, title, RedMessageBox.Mode.Warning );

            if ( setVersionConfirmed )
            {
                string sql = $@"BEGIN TRANSACTION [TransSetLauncherMinVersion]
                                BEGIN TRY
                               
                                -- Restore the keep-till date of the old min version
                                DECLARE @previousBuildId int;
                                DECLARE @original_date datetime;
                                DECLARE @replacement_date datetime;
                                DECLARE @resolved_date datetime;
                               
                                SELECT
                               
                                    @previousBuildId = (SELECT TOP 1 [build_num] FROM [{Settings.DatabaseName}].[dbo].[LauncherMinVersion] ORDER BY [created] desc),
                               
	                                -- The date the build was originally scheduled to be deleted was date-added + 3 months
	                                @original_date = dateadd( mm, 3, ( select [ADDED] from [buildsystem].[dbo].[BUILDS_ARCHIVE_TEST] where build_num = @previousBuildId ) ),
                               
	                                -- If we're already past that date (so it doesn't get deleted immediately) let's just give it 5 days from today
	                                @replacement_date = dateadd( dd, 5, getdate() ),
                               
                                    @resolved_date = ( case when @original_date > @replacement_date then @original_date else @replacement_date end )
                               
                                UPDATE [buildsystem].[dbo].[BUILDS_ARCHIVE] SET
	                                    KEEP_TILL = @resolved_date,
                                        RAW_TILL = @resolved_date,
                                        ARCHIVE_TILL = @resolved_date
	                                    WHERE build_num = @previousBuildId
                               
                                -- Tell the build system to keep the new build around indefinitely
                                UPDATE [buildsystem].[dbo].[BUILDS_ARCHIVE] SET
	                                    KEEP_TILL = NULL,
                                        RAW_TILL = NULL,
                                        ARCHIVE_TILL = NULL
	                                    WHERE build_num = '{pkg.BuildId}'
                               
                                -- Update the database with the new minimum version
                                INSERT INTO [{Settings.DatabaseName}].[dbo].[LauncherMinVersion] ( [build_num], [users_id] ) VALUES ('{pkg.BuildId}', '{_toolkitPackages.Manager.User.Id}')
                               
                                COMMIT TRANSACTION [TransSetLauncherMinVersion]
                                END TRY
                                BEGIN CATCH
                                    ROLLBACK TRANSACTION [TransSetLauncherMinVersion]
                               
                                    DECLARE @ErrorMessage NVARCHAR( 4000 );
                                    DECLARE @ErrorSeverity INT;
                                    DECLARE @ErrorState INT;
                               
                                    SELECT
                                        @ErrorMessage = ERROR_MESSAGE(),
                                                    @ErrorSeverity = ERROR_SEVERITY(),
                                                    @ErrorState = ERROR_STATE();
                               
                                    -- Use RAISERROR inside the CATCH block to return error
                                    -- information about the original error that caused
                                    -- execution to jump to the CATCH block.
                                    RAISERROR( @ErrorMessage, --Message text.
                                                @ErrorSeverity, --Severity.
                                                @ErrorState -- State.
                               
                                                );
                                    END CATCH";

                await DatabaseHelper.ExecuteAsync( sql, Settings.DatabaseConnectionString );
            }
        }

        #endregion Event Handlers

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged( [CallerMemberName] string propertyName = null )
        {
            PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
        }

        #endregion INotifyPropertyChanged
    }
}
